﻿#region Copyright and Licensing Notice
// Copyright (C) 2011 Steven Bosnick
// 
// This program is open source software; you can redistribute it 
// and/or modifiy it under the terms of the Eclipse Public License,
// version 1.0.
// 
// This program is distributed in the hope that it will be useful, 
// but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A 
// PARTICULAR PURPOSE. See the Eclipse Public License for more
// details.
// 
// You should have received a copy of the Eclipse Public License 
// along with this program; if not, you can find a copy at
// "http://www.opensource.org/licenses/eclipse-1.0.php".
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using BitwiseOre.SharedMemory.Core.Common;
using BitwiseOre.SharedMemory.JetBrains;

namespace BitwiseOre.SharedMemory.Core.IPCQueues
{
    [UsedImplicitly]
    internal sealed class ConnectionQueuesFactory : ISharedFactory<ConnectionQueues>, ISharedObjectInit
    {
        public delegate IConnectionBuffers CreateConnectionBuffers(int count, int bufferSize);

        public delegate IConnectionBuffers OpenConnectionBuffers();

        private readonly Func<int, int, ISemaphore> _createSemaphore;
        private readonly OpenSemaphore _openSemaphore;
        private readonly CreateConnectionBuffers _createConnectionBuffers;
        private readonly OpenConnectionBuffers _openConnectionBuffers;
        private readonly IConnectionQueuesConfig _config;

        private enum SemaphoreType
        {
            ReadNotEmpty,
            ReadNotFull,
            WriteNotEmpty,
            WriteNotFull
        }

        private IEnumerable<ISemaphore> _semaphores;
        private IConnectionBuffers _connectionBuffers;
        private IDictionary<SemaphoreType, int> _semaphoreMap;

        public ConnectionQueuesFactory(ISemaphoreFactory semaphoreFactory, OpenSemaphore openSemaphore, CreateConnectionBuffers createConnectionBuffers, OpenConnectionBuffers openConnectionBuffers, IConnectionQueuesConfig config)
        {
            _createSemaphore = (initial, maxiumum) => semaphoreFactory.Create(initial, maxiumum);
            _openSemaphore = openSemaphore;
            _openConnectionBuffers = openConnectionBuffers;
            _createConnectionBuffers = createConnectionBuffers;
            _config = config;
        }


        public void InitializeNew()
        {
            _semaphores = from i in Enumerable.Range(0, 4)
                          select _createSemaphore(i % 2 == 0 ? 0 : _config.Count, _config.Count);
            _connectionBuffers = _createConnectionBuffers(_config.Count, _config.BufferSize);
            _semaphoreMap = new Dictionary<SemaphoreType, int>
                                {
                                    {SemaphoreType.ReadNotEmpty, 0},
                                    {SemaphoreType.ReadNotFull, 1},
                                    {SemaphoreType.WriteNotEmpty, 2},
                                    {SemaphoreType.WriteNotFull, 3}
                                };
        }

        public void InitializeExisting()
        {
            _semaphores = from i in Enumerable.Range(0, 4)
                          select _openSemaphore();
            _connectionBuffers = _openConnectionBuffers();
            _semaphoreMap = new Dictionary<SemaphoreType, int>
                                {
                                    {SemaphoreType.ReadNotEmpty, 2},
                                    {SemaphoreType.ReadNotFull, 3},
                                    {SemaphoreType.WriteNotEmpty, 0},
                                    {SemaphoreType.WriteNotFull, 1}
                                };
        }

        public ConnectionQueues Create()
        {
            var semaphores = _semaphores.ToArray();

            var readbuffers = new GuardedEnumerator<IReadBuffer>(
                semaphores[_semaphoreMap[SemaphoreType.ReadNotEmpty]],
                semaphores[_semaphoreMap[SemaphoreType.ReadNotFull]],
                _connectionBuffers.ReadBuffers.GetEnumerator());

            var writebuffers = new GuardedEnumerator<IWriteBuffer>(
                semaphores[_semaphoreMap[SemaphoreType.WriteNotFull]],
                semaphores[_semaphoreMap[SemaphoreType.WriteNotEmpty]],
                _connectionBuffers.WriteBuffers.GetEnumerator());

            return new ConnectionQueues(writebuffers, readbuffers);
        }
    }
}